/*
* This file is part of QSerialDevice, an open-source cross-platform library
* Copyright (C) 2009  Denis Shienkov
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Contact Denis Shienkov:
*          e-mail: <scapig2@yandex.ru>
*             ICQ: 321789831
*/

#include <QtCore/private/qwineventnotifier_p.h>
#include "serialnotifier.h"
#include "serialnotifier_p.h"

//#define SERIALNOTIFIER_WIN_DEBUG

#ifdef SERIALNOTIFIER_WIN_DEBUG
#include <QtCore/QDebug>
#endif


SerialNotifierPrivate::SerialNotifierPrivate()
        : m_enabled(false), m_eventNotifier(0), m_eventMask(0)
{
}

SerialNotifierPrivate::~SerialNotifierPrivate()
{
    this->setEnabled(false);

    if (this->m_ovl.hEvent) {
        if ( 0 == ::CloseHandle(this->m_ovl.hEvent) ) {
        }
        this->m_ovl.hEvent = 0;
    }
}

void SerialNotifierPrivate::init(Qt::HANDLE descriptor, SerialNotifier::Type type)
{
    Q_Q(SerialNotifier);

    this->m_descriptor = descriptor;
    this->m_type = type;

    if ( INVALID_HANDLE_VALUE == this->m_descriptor )
        return;

    ::memset(&this->m_ovl, 0, sizeof(this->m_ovl));
    this->m_ovl.hEvent = ::CreateEvent(0, false, false, 0);

    if ( 0 == this->m_ovl.hEvent )
        return;

    this->m_eventNotifier = new QWinEventNotifier(this->m_ovl.hEvent, q);
    QObject::connect(this->m_eventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_updateNotifier()));
}

void SerialNotifierPrivate::setEnabled(bool enable)
{
    if (!this->isValid())
        return;

    if (enable) {
        if ( 0 == ::GetCommMask(this->m_descriptor, &this->m_eventMask) )
            return;

        switch (this->m_type) {
        case SerialNotifier::Read:
            this->m_eventMask |= EV_RXCHAR; break;
        case SerialNotifier::Write:
            this->m_eventMask |= EV_TXEMPTY; break;
        case SerialNotifier::Exception:
            this->m_eventMask |= EV_ERR; break;
        case SerialNotifier::CTS:
            this->m_eventMask |= EV_CTS; break;
        case SerialNotifier::DSR:
            this->m_eventMask |= EV_DSR; break;
        case SerialNotifier::Ring:
            this->m_eventMask |= EV_RING; break;
        default: return;
        }

        if ( 0 == ::SetCommMask(this->m_descriptor, this->m_eventMask) )
            return;

        ::WaitCommEvent(this->m_descriptor, &this->m_eventMask, &this->m_ovl);
    }

    this->m_eventNotifier->setEnabled(enable);
    this->m_enabled = enable;
}

void SerialNotifierPrivate::_q_updateNotifier()
{
    Q_Q(SerialNotifier);

    if (!this->isValid())
        return;

    bool result = false;

    switch (this->m_type) {
    case SerialNotifier::Read: {
            if (EV_RXCHAR & this->m_eventMask) {
                DWORD err = 0;
                COMSTAT cs;
                ::memset(&cs, 0, sizeof(cs));

                if ( (0 == ::ClearCommError(this->m_descriptor, &err, &cs) )
                    || err ) {
                }
                else {
                    if (cs.cbInQue)
                        result = true;
                }
            }
        }
        break;
    case SerialNotifier::Write:
        if (EV_TXEMPTY & this->m_eventMask)
            result = true;
        break;
    case SerialNotifier::Exception:
        if (EV_ERR & this->m_eventMask)
            result = true;
        break;
    case SerialNotifier::CTS:
        if (EV_CTS & this->m_eventMask)
            result = true;
        break;
    case SerialNotifier::DSR:
        if (EV_DSR & this->m_eventMask)
            result = true;
        break;
    case SerialNotifier::Ring:
        if (EV_RING & this->m_eventMask)
            result = true;
        break;
    default: return;
    }

    if (result)
        emit q->activated();

    this->setEnabled(this->m_enabled);
}

bool SerialNotifierPrivate::isValid() const
{
    return ( (INVALID_HANDLE_VALUE != this->m_descriptor)
             && (0 != this->m_ovl.hEvent)
             && (0 != this->m_eventNotifier) );
}

#include "moc_serialnotifier.cpp"



